Option Explicit

' ======================================================================
' Descriptor
' SQL 格式化器
' ======================================================================

' ======================================================================
' Field
' ======================================================================

' 格式化器的 sql 解析器
Private analyzer As SqlFormatterAnalyzer

' 格式化后的 sql 原始字符串集合
' IPrimitiveArrayRef<String>
Private formattedSqlStrArr As IPrimitiveArrayRef

' 格式化后的字符串
Private formattedSqlStr As String

' ==================================================
' 初始化时需要加载的内容
' ==================================================
Private config As SqlFormatterConfiguration

' ======================================================================
' 初始化方法
' ======================================================================
Private Sub Class_Initialize()
End Sub

Private Sub Class_Terminate()
    Set analyzer = Nothing
    Set formattedSqlStrArr = Nothing
    Set config = Nothing
End Sub

' ======================================================================
' Init
' ======================================================================
' 初始化 SqlFormatter
'
' @param configVal 格式化配置对象
Public Sub Init(ByRef configVal As SqlFormatterConfiguration)
    Set config = configVal

    Set analyzer = New SqlFormatterAnalyzer
    analyzer.Init config
End Sub

' ======================================================================
' 辅助方法
' ======================================================================
' 清除格式化器中的缓存
Private Sub clearCacheData()
    ' 1. 清除格式化后的 sql 原始字符串集合
    Set formattedSqlStrArr = Nothing
    ' 2. 清除格式化后的 sql 字符串
    formattedSqlStr = ""
End Sub

' ======================================================================
' 格式化sql的整体驱动
' ======================================================================
' 解析方式1: 通过 sql 字符串完成解析
'
' @param sql  需要格式化的 sql
Public Sub analyze(ByRef sql As String)
    ' ==================================================
    ' 1. 清除缓存
    ' ==================================================
    clearCacheData

    ' ==================================================
    ' 2. 执行 sql 解析
    ' ==================================================
    analyzer.analyze sql
End Sub

' 直接解析 sql token 数组
'
' @param meanTokens  需要格式化的 sql token 数组 (IObjArrayRef<SqlToken>)
Public Sub analyzeTokens(ByRef meanTokens As IObjArrayRef)
    ' ==================================================
    ' 1. 清除缓存
    ' ==================================================
    clearCacheData

    ' ==================================================
    ' 2. 执行 sql 解析
    ' ==================================================
    analyzer.analyzeTokensmeanTokens
End Sub

' 生成格式化字符串
'
' @return 格式化后的字符串
Public Function getSql() As String
    ' 1. 如果格式化 sql 字符串已经存在了，则返回
    If formattedSqlStr <> "" Then
        ' return formattedSqlStr
        getSql = formattedSqlStr
        Exit Function
    End If

    ' 2. 如果 sql 的原始字符串集合没有生成过，则立刻生成
    If formattedSqlStrArr Is Nothing Then
        createFormattedSqlStrArr
    End If

    ' 3. 生成结果
    ' 如果原始字符串为空，则返回空字符串
    If formattedSqlStrArr Is Nothing Then
        ' return ""
        getSql = ""
        Exit Function
    End If
    If formattedSqlStrArr.Count = 0 Then
        ' return ""
        getSql = ""
        Exit Function
    End If

    ' 4. 用换行符连接结果
    formattedSqlStr = UtilsArrayRef.reducePrimitiveArrayRefToStr(formattedSqlStrArr, config.lineBreak)

    ' return formattedSqlStr
    getSql = formattedSqlStr
End Function

' 获取格式化后的 sql 字符串集合
'
' @return 格式化后的字符串 (IPrimitiveArrayRef<String>)
Public Function getSqlStrArray() As IPrimitiveArrayRef
    ' 1. 如果 sql 的原始字符串集合没有生成过，则立刻生成
    If formattedSqlStrArr Is Nothing Then
        createFormattedSqlStrArr
    End If

    Set getSqlStrArray = formattedSqlStrArr
End Function

' 获取格式化后的 sql 字符串所占的行数
'
' @return 格式化后的 sql 字符串所占的行数
Public Function getSqlLineSize() As Long
    ' 1. 如果 sql 的原始字符串集合没有生成过，则立刻生成
    If formattedSqlStrArr Is Nothing Then
        createFormattedSqlStrArr
    End If

    ' 2. 如果 sql 的原始字符串集合已经存在了，则返回集合的行数
    If formattedSqlStrArr Is Nothing Then
        ' return 0
        Exit Function
    Else
        ' return formattedSqlStrArr.Count()
        getSqlLineSize = formattedSqlStrArr.Count
        Exit Function
    End If
End Function

' 生成所有作用域的字符串集合
Private Sub createFormattedSqlStrArr()
    Dim elem As ISqlFormatterElem

    ' 1. 从解析器中获取 sql 的解析结果
    ' IObjArrayRef<ISqlFormatterElem> sqlEnvs
    Dim sqlEnvs As IObjArrayRef
    Set sqlEnvs = analyzer.getSqlEnvs
    '    如果没有任何解析结果，则退出
    If sqlEnvs Is Nothing Then
        ' return
        Exit Sub
    End If

    ' 2. 作用域对齐
    ' 2.1. 检查配置对象的对齐级别: 如果需要对齐，则继续
    Dim i As Long

    ' IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>> groups
    Dim groups As IObjArrayRef
    If config.alignmentLevel <> SqlAlignmentLevel.NO_ALIGNMENT Then
        ' 2.2 获取对齐器
        Dim aligner As SqlFormatterAligner: Set aligner = config.getAligner

        For i = 0 To sqlEnvs.MaxIndex
            Set elem = sqlEnvs.Item(i)

            ' 2.3. 从作用域中对齐元素分组集合，保存到 `groups`
            Set groups = elem.getAlignmentElemGroups
            ' 2.4. 对齐所有的分组
            aligner.alignmentElem groups
        Next
    End If

    ' 3. 如果只有一个作用域，则直接生成
    If sqlEnvs.Count = 1 Then
        Set elem = sqlEnvs.Item(0)

        ' 生成结果，并给最后一行添加 ';'
        Set formattedSqlStrArr = elem.toStringArray(config)

        If formattedSqlStrArr.Count > 0 Then
            ' 检查: 如果配置中需要在末尾添加分号，则继续
            If config.needSqlEndSemicolon Then
                ' 检查: 如果 elem 是以注释结尾的，则新增一行[;]
                If elem.isEndsWithComment Then
                    formattedSqlStrArr.Add ";"

                ' 否则，在最后一行的末尾添加[;]
                Else
                    formattedSqlStrArr.setVal _
                        formattedSqlStrArr.MaxIndex, _
                        formattedSqlStrArr.getLast & ";"
                End If
            End If
        End If

    ' 4. 如果有多个则需要遍历生成
    Else
        ' 创建结果集
        Set formattedSqlStrArr = UtilsArrayRef.createRealPrimitiveArrayRef(100)

        ' 遍历作用域集合，生成每一个作用域的字符串集合，并合并
        Dim strArray As IPrimitiveArrayRef

        For i = 0 To sqlEnvs.MaxIndex
            Set elem = sqlEnvs.Item(i)
            Set strArray = elem.toStringArray(config)

            ' 如果结果为空，则跳过
            If strArray Is Nothing Then
                GoTo loopContinue
            End If

            If strArray.Count = 0 Then
                GoTo loopContinue
            End If

            ' 检查: 如果配置中需要在末尾添加分号，则继续
            If config.needSqlEndSemicolon Then
                ' 在末尾添加 [;]，然后保存
                ' 如果这个元素是以注释结尾的，则在原始的字符串集合后，在新增一行，并将这一行设置为 [;]
                If elem.isEndsWithComment Then
                    formattedSqlStrArr.mergeArrRef strArray
                    formattedSqlStrArr.Add ";"

                ' 否则，直接将 [;] 保存到最后一行的末尾
                Else
                    strArray.setVal _
                        strArray.MaxIndex, _
                        strArray.getLast() & ";"

                    formattedSqlStrArr.mergeArrRef strArray
                End If

            ' 否则
            Else
                ' 保存原始字符串数组
                formattedSqlStrArr.mergeArrRef strArray

                ' 除了最后一个 sql，每个sql后面都需要额外附加一个换行符
                If i < sqlEnvs.MaxIndex Then
                    ' 通过增加一个空字符串，来增加一个换行
                    formattedSqlStrArr.Add ""
                End If
            End If
loopContinue:
        Next
    End If
End Sub